package com.zxy.ziems.server.energy.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.zxy.btp.mybatis.base.BtpBaseServiceImpl;
import com.zxy.ziems.server.energy.convertor.EnergyPriceTimeConvertor;
import com.zxy.ziems.server.energy.mapper.EnergyPriceMapper;
import com.zxy.ziems.server.energy.mapper.EnergyPriceTimeMapper;
import com.zxy.ziems.server.energy.pojo.dto.EnergyPriceTagDTO;
import com.zxy.ziems.server.energy.pojo.dto.EnergyPriceTimeDTO;
import com.zxy.ziems.server.energy.pojo.entity.EnergyPriceEntity;
import com.zxy.ziems.server.energy.pojo.entity.EnergyPriceTimeEntity;
import com.zxy.ziems.server.energy.pojo.vo.EnergyPriceTagVO;
import com.zxy.ziems.server.energy.pojo.vo.EnergyPriceTimeVO;
import com.zxy.ziems.server.energy.service.EnergyPriceTimeService;
import com.zxy.ziems.server.log.DataRecordTypeEnum;
import com.zxy.ziems.server.log.pojo.entity.DataRecordLogEntity;
import com.zxy.ziems.server.log.service.DataRecordService;
import com.zxy.ziems.server.mybatis.core.query.QueryWrapperX;
import com.zxy.ziems.server.utils.AssertUtil;
import com.zxy.ziems.server.utils.BusinessExceptionUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.sql.Time;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

import static com.zxy.ziems.server.constant.RedisKeyConstant.GET_DATA_TYPE_PRICE_;

@Service
public class EnergyPriceTimeServiceImpl extends BtpBaseServiceImpl<EnergyPriceTimeMapper, EnergyPriceTimeEntity> implements EnergyPriceTimeService {

    @Autowired
    private EnergyPriceMapper energyPriceMapper;
    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    private DataRecordService dataRecordService;

    @Override
    public List<EnergyPriceTagVO> getEnergyPriceTime(String energyPriceId) {
        List<EnergyPriceTagVO> res = new ArrayList<>();
        List<EnergyPriceTimeEntity> energyPriceTimeEntities = baseMapper.selectList(EnergyPriceTimeEntity::getEnergyPriceId, energyPriceId);
        Map<String, List<EnergyPriceTimeEntity>> tagMap = energyPriceTimeEntities.stream().collect(Collectors.groupingBy(EnergyPriceTimeEntity::getTag));
        tagMap.forEach((k, v) -> {
            EnergyPriceTagVO vo = new EnergyPriceTagVO();
            List<EnergyPriceTimeVO> times = EnergyPriceTimeConvertor.INSTANT.entityList2VoList(v);
            vo.setTag(k);
            vo.setTimes(times);
            EnergyPriceTimeEntity energyPriceTimeEntity = v.get(0);
            vo.setEnergyPriceId(energyPriceTimeEntity.getEnergyPriceId());
            vo.setPrice(energyPriceTimeEntity.getPrice());
            res.add(vo);
        });
        return res;
    }

    private void removeCache() {
        Set<String> keys = redisTemplate.keys(GET_DATA_TYPE_PRICE_ + "*");
        redisTemplate.delete(keys);
    }

    @Override
    public void removeByEnergyPriceId(String energyPriceId) {
        QueryWrapper<EnergyPriceTimeEntity> deleteParam = new QueryWrapperX<>();
        deleteParam.lambda().eq(EnergyPriceTimeEntity::getEnergyPriceId, energyPriceId);
        removeCache();
        baseMapper.delete(deleteParam);
    }

    @Transactional
    @Override
    public void addOrModifyTime(List<EnergyPriceTagDTO> energyPriceTagDTOs) {
        List<EnergyPriceTagVO> old = getEnergyPriceTime(energyPriceTagDTOs.get(0).getEnergyPriceId());
        doCheckBeforeModifyAction(energyPriceTagDTOs);
        removeCache();
        addLog(old, energyPriceTagDTOs);
        for (EnergyPriceTagDTO energyPriceTagDTO : energyPriceTagDTOs) {
            List<EnergyPriceTimeEntity> energyPriceTimes = tagDTOListToTimeEntityList(energyPriceTagDTO.getTimes(),
                    energyPriceTagDTO.getEnergyPriceId(), energyPriceTagDTO.getPrice(), energyPriceTagDTO.getTag());
            saveBatch(energyPriceTimes);
        }

    }

    private void addLog(List<EnergyPriceTagVO> old, List<EnergyPriceTagDTO> energyPriceTagDTOs) {
        EnergyPriceEntity entity = energyPriceMapper.selectById(energyPriceTagDTOs.get(0).getEnergyPriceId());
        Map<String, BigDecimal> oldMap = old.stream().collect(Collectors.toMap(EnergyPriceTagVO::getTag, EnergyPriceTagVO::getPrice));
        Map<String, BigDecimal> newMap = energyPriceTagDTOs.stream().collect(Collectors.toMap(EnergyPriceTagDTO::getTag, EnergyPriceTagDTO::getPrice));
        List<DataRecordLogEntity> logs = new ArrayList<>();
        newMap.forEach((k, v) -> {
            BigDecimal o = oldMap.get(k);
            if (v.compareTo(o) != 0) {
                DataRecordLogEntity log = new DataRecordLogEntity();
                log.setTag(k);
                log.setOldData(o == null ? "" : o.toString());
                log.setNewData(v.toString());
                logs.add(log);
            }
        });
        dataRecordService.add(DataRecordTypeEnum.ENERGY_PRICE_MODIFY, entity.getName(), entity.getId(), logs);
    }

    private List<EnergyPriceTimeEntity> tagDTOListToTimeEntityList(List<EnergyPriceTimeDTO> times, String energyPriceId, BigDecimal price, String tag) {
        return times.stream().map(m -> {
            EnergyPriceTimeEntity entity = EnergyPriceTimeConvertor.INSTANT.dto2Entity(m);
            entity.setPrice(price);
            entity.setEnergyPriceId(energyPriceId);
            entity.setTag(tag);
            entity.setId(null);
            return entity;
        }).collect(Collectors.toList());
    }

    private void doCheckBeforeModifyAction(List<EnergyPriceTagDTO> energyPriceTagDTOs) {
        long count = energyPriceTagDTOs.stream().map(EnergyPriceTagDTO::getEnergyPriceId).collect(Collectors.toSet()).stream().count();
        AssertUtil.isTrue(count == 1, "不支持多单价配置");
        EnergyPriceTagDTO energyPriceTagDTO = energyPriceTagDTOs.get(0);
        removeByEnergyPriceId(energyPriceTagDTO.getEnergyPriceId());

        EnergyPriceEntity energyPriceEntity = energyPriceMapper.selectById(energyPriceTagDTO.getEnergyPriceId());
        AssertUtil.nonNull(energyPriceEntity, "未找到能源单价");
        AssertUtil.isFalse(energyPriceEntity.getSingleStatus(), "单一价格不需要配置分段");

        List<EnergyPriceTimeDTO> times = new ArrayList<>();
        for (EnergyPriceTagDTO dto : energyPriceTagDTOs) {
            times.addAll(dto.getTimes());
        }
        Long reduce = times.stream().map(m -> {
            return getTimeDifference(m.getEndTime(), m.getStartTime());
        }).reduce(0L, (a, b) -> a + b);
        Integer dayTime = 24 * 60 * 60;
        if (reduce < dayTime-times.size()) {
            throw BusinessExceptionUtil.exception("时间段不满足一天");
        }
        for (int i = 0; i < times.size(); i++) {
            EnergyPriceTimeDTO time = times.get(i);
            AssertUtil.isFalse(time.getEndTime().toString().equals("00:00:00"), "此时间不可为结束时间");
            for (int j = 0; j < times.size(); j++) {
                if (j == i) continue;
                EnergyPriceTimeDTO old = times.get(j);
                if ((old.getStartTime().compareTo(time.getStartTime()) > -1
                        && old.getStartTime().compareTo(time.getEndTime()) < 1)
                        || (old.getEndTime().compareTo(time.getStartTime()) > -1
                        && old.getEndTime().compareTo(time.getEndTime()) < 1)) {
                    throw BusinessExceptionUtil.exception("时间段不可重复配置");
                }
//                AssertUtil.isFalse(old.getName().equals(time.getName()), "分段名称已存在");
            }
        }
    }

    public static long getTimeDifference(Time earlierTime, Time laterTime) {
        // 将java.sql.Time转换为对应的java.util.Date对象
        Date date1 = new Date(earlierTime.getTime());
        Date date2 = new Date(laterTime.getTime());

        // 计算时间差（毫秒）
        return Math.abs(date2.getTime() - date1.getTime()) / 1000;
    }

    @Override
    public BigDecimal getEnergyPrice(String energyPriceId, LocalDateTime localDateTime) {
        Time time = new Time(localDateTime.getHour(), localDateTime.getMinute(), localDateTime.getSecond());
        QueryWrapper<EnergyPriceTimeEntity> param = new QueryWrapper<>();
        param.lambda().eq(EnergyPriceTimeEntity::getEnergyPriceId, energyPriceId)
                .le(EnergyPriceTimeEntity::getStartTime, time)
                .ge(EnergyPriceTimeEntity::getEndTime, time);
        EnergyPriceTimeEntity entity = baseMapper.selectOne(param);
        return entity == null ? null : entity.getPrice();
    }
}
